---
title: 記憶體管理基礎
---

記憶體管理是 C++ 程式設計的關鍵環節，它提供了對記憶體如何分配和釋放的精細控制。這與 JavaScript 的自動垃圾回收機制有顯著不同。

## 堆疊記憶體 vs. 堆積記憶體

在 C++ 中，記憶體主要在兩個區域進行管理：堆疊和堆積。

### 堆疊記憶體
*   **自動分配：** 用於局部變數、函式參數和返回位址。
*   **後進先出 (LIFO)：** 記憶體以嚴格的順序分配和釋放。
*   **快速存取：** 分配和釋放速度非常快。
*   **大小有限：** 通常比堆積記憶體小。
*   **生命週期：** 堆疊上的變數在其超出作用域時會自動釋放。

<UniversalEditor title="堆疊記憶體範例" compare={true}>
```javascript !! js
// JavaScript: 原始類型和函式呼叫的堆疊行為
function calculateSum(a, b) {
  let result = a + b; // 'result' 在概念上位於 calculateSum 的堆疊框架上
  return result;
}

let x = 10; // 'x' 在堆疊上
let y = 20; // 'y' 在堆疊上
let sum = calculateSum(x, y); // 函式呼叫新增一個堆疊框架
```

```cpp !! cpp
// C++: 堆疊記憶體
#include <iostream>

int calculateSum(int a, int b) {
  int result = a + b; // 'result' 在堆疊上分配
  return result;
} // 'result' 在 calculateSum 返回時釋放

int main() {
  int x = 10; // 'x' 在堆疊上分配
  int y = 20; // 'y' 在堆疊上分配
  int sum = calculateSum(x, y); // calculateSum 新增一個堆疊框架
  // std::cout << "Sum: " << sum << std::endl;
  return 0;
} // 'x'、'y'、'sum' 在 main 返回時釋放
```
</UniversalEditor>

### 堆積記憶體
*   **動態分配：** 用於在編譯時大小未知或生命週期超出函式作用域的資料。
*   **手動管理：** 程式設計師必須使用 `new` 和 `delete`（或 `malloc`/`free`）明確地分配和釋放記憶體。
*   **較慢存取：** 分配和釋放速度比堆疊記憶體慢。
*   **較大大小：** 通常比堆疊記憶體大得多。
*   **生命週期：** 記憶體會一直存在，直到明確釋放或程式終止。

<UniversalEditor title="堆積記憶體範例" compare={true}>
```javascript !! js
// JavaScript: 物件在堆積上分配
let obj = { name: "Alice" }; // 'obj' 引用在堆疊上，物件資料在堆積上

// 當 'obj' 不再被引用時，垃圾回收器會回收記憶體
obj = null; 
```

```cpp !! cpp
// C++: 堆積記憶體
#include <iostream>

int main() {
  int* dynamicInt = new int; // 在堆積上分配一個整數
  *dynamicInt = 100;
  // std::cout << "Dynamic Int: " << *dynamicInt << std::endl;

  delete dynamicInt; // 釋放記憶體
  dynamicInt = nullptr; // 釋放後將指標設定為 nullptr 是個好習慣

  // 在堆積上分配一個陣列
  int* dynamicArray = new int[5];
  for (int i = 0; i < 5; ++i) {
    dynamicArray[i] = i * 10;
  }
  // 陣列需要使用 delete[] 釋放
  delete[] dynamicArray;
  dynamicArray = nullptr;

  return 0;
}
```
</UniversalEditor>

## 變數生命週期

*   **自動 (堆疊) 變數：** 在進入其作用域時建立，並在其作用域退出時銷毀。
*   **動態 (堆積) 變數：** 使用 `new` 建立，並持續存在直到明確地 `delete`。
*   **靜態變數：** 在程式啟動時分配一次，並在程式的整個執行期間持續存在。
*   **全域變數：** 類似於靜態變數，在程式啟動時分配，並在程式的生命週期內持續存在。

## 記憶體佈局基礎

典型的 C++ 程式記憶體佈局包括：
*   **文字/程式碼區段：** 儲存可執行指令。
*   **資料區段：** 儲存全域和靜態變數（已初始化和未初始化）。
*   **堆積：** 動態分配的記憶體。
*   **堆疊：** 局部變數和函式呼叫資訊。

## 與 JavaScript 記憶體模型的比較

JavaScript 使用**垃圾回收器 (GC)** 自動管理記憶體。當一個物件不再可達（不再被程式的任何部分引用）時，GC 會自動回收記憶體。這使開發人員無需手動記憶體管理，減少了記憶體洩漏和懸空指標等常見錯誤。

C++ 則需要**手動記憶體管理**。開發人員負責在需要時分配記憶體，並在不再需要時釋放記憶體。未能釋放記憶體會導致**記憶體洩漏**，而嘗試存取已釋放的記憶體會導致**懸空指標**或**使用後釋放**錯誤。

## 記憶體洩漏概念

**記憶體洩漏**發生在程式在堆積上分配記憶體，但在不再需要時未能釋放它。這會導致可用記憶體逐漸消耗，可能導致程式甚至整個系統變慢或崩潰。

**C++ 中記憶體洩漏的常見原因：**
*   忘記 `delete` 使用 `new` 分配的記憶體。
*   遺失動態分配記憶體的指標。
*   在錯誤路徑或例外情況下錯誤地處理記憶體。

## 基本記憶體管理原則

1.  **需要時分配，完成時釋放：** 每個 `new` 都必須有對應的 `delete`（或 `new[]` 與 `delete[]`）。
2.  **所有權：** 明確定義程式碼的哪個部分負責管理一段記憶體。
3.  **RAII (資源取得即初始化)：** 一種 C++ 程式設計慣用語，將資源（如記憶體）的生命週期與物件的生命週期綁定。當物件超出作用域時，其解構函式會自動釋放資源。智慧指標是 RAII 的主要範例。

## 偵錯記憶體問題

偵錯 C++ 中的記憶體問題可能具有挑戰性。常用的工具包括：
*   **Valgrind (Linux/macOS)：** 一個強大的記憶體偵錯器，可以偵測記憶體洩漏、使用後釋放錯誤和其他記憶體相關問題。
*   **AddressSanitizer (Clang/GCC)：** 一個整合到編譯器中的快速記憶體錯誤偵測器。
*   **Visual Studio 診斷工具 (Windows)：** Visual Studio 中用於記憶體分析和洩漏偵測的內建工具。

---

### 練習題：
1.  解釋 C++ 中堆疊記憶體和堆積記憶體之間的關鍵差異。何時會選擇在堆積而不是堆疊上分配記憶體？
2.  什麼是記憶體洩漏，它在 C++ 中如何發生？JavaScript 的記憶體管理方法如何緩解這個問題？
3.  描述 RAII 原則以及它如何幫助 C++ 記憶體管理。

### 專案構想：
*   編寫一個 C++ 程式，模擬一個簡單的鏈結串列。實作新增節點、移除節點和列印串列的函式。密切注意記憶體分配和釋放，以避免記憶體洩漏。明確使用 `new` 和 `delete`。
